// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: xguo

#pragma once

#include <iostream>
#include <limits>
#include <list>
#include <queue>
#include <string>
#include <vector>

#include "coin2/strategy/umm/base.h"
#include "coin2/strategy/umm/umm.pb.h"

namespace xguo {

struct BuySellPair {
  int64_t buy_timestamp;
  int64_t sell_timestamp;

  double fill_buy_price;
  double fill_sell_price;
  double fill_qty;  // fill_qty > 0
  double pnl;
  double pnl_bp;

  BuySellPair() {
    std::memset(this, 0, sizeof(*this));
  }
};

struct FillStatsConfig {
  uint64_t product_id;
  double maker_fee_rate;
  double taker_fee_rate;
  double walk_step;
  double lot_size;

  FillStatsConfig() {
    std::memset(this, 0, sizeof(*this));
  }
};

class FillStats {
 public:
  explicit FillStats(const FillStatsConfig&);

  void UpdateByFill(const Fill&);
  void UpdateByBbo(const Bbo&);
  void UpdateByPos(double pos);

  void set_timestamp(int64_t ts) {
    // update timestamp
    current_timestamp_ = ts;
  }

  int64_t timestamp() const {
    // timestamp
    return current_timestamp_;
  }

  int64_t last_fill_timestamp() const {
    // last fill time
    return last_fill_timestamp_;
  }

  int64_t last_buy_fill_timestamp() const {
    // last fill time
    return last_buy_fill_timestamp_;
  }

  int64_t last_sell_fill_timestamp() const {
    // last fill time
    return last_sell_fill_timestamp_;
  }

  double walk_step() const {
    // walk_step
    return config_.walk_step;
  }

  double position() const {
    return position_;
  }

  bool is_qty_too_small(double x) const {
    return std::abs(x) < 1e-08;
  }

  auto consecutive_buy_fill_qty() const {
    return consecutive_buy_fill_qty_;
  }

  auto consecutive_sell_fill_qty() const {
    return consecutive_sell_fill_qty_;
  }

  auto consecutive_buy_fill_count() const {
    return consecutive_buy_fill_qty_ / config_.lot_size;
  }

  auto consecutive_sell_fill_count() const {
    return -consecutive_sell_fill_qty_ / config_.lot_size;
  }

  auto fee() const {
    // fee
    return fee_;
  }

  auto pnl() const {
    // pnl
    return -buy_amount_ - sell_amount_ + (buy_qty_ + sell_qty_) * bbo_.midp;
  }

  auto buy_lose_qty() const {
    return buy_lose_qty_;
  }

  auto buy_lose_pnl() const {
    return buy_lose_pnl_;
  }

  auto buy_profit_qty() const {
    return buy_profit_qty_;
  }

  auto buy_profit_pnl() const {
    return buy_profit_pnl_;
  }

  auto sell_lose_qty() const {
    return sell_lose_qty_;
  }

  auto sell_lose_pnl() const {
    return sell_lose_pnl_;
  }

  auto sell_profit_qty() const {
    return sell_profit_qty_;
  }

  auto sell_profit_pnl() const {
    return sell_profit_pnl_;
  }

  auto exposure_qty() const {
    return bfqty_ + sfqty_;
  }

  auto exposure_cost() const {
    return bfamt_ + sfamt_;
  }

  auto exposure_avg_price() const {
    return std::fabs(exposure_cost() / exposure_qty());
  }

  const Fill* highest_fill_sell() const;
  const Fill* lowest_fill_buy() const;

  std::string DebugString() const;
  coin2::strategy::umm::FillStatsProto DebugProto() const;

 private:
  void MatchFill(Fill* fill);

  int64_t current_timestamp_{0};
  int64_t last_fill_timestamp_{0};
  double buy_qty_{0};
  double buy_amount_{0};
  double sell_qty_{0};
  double sell_amount_{0};
  double position_{0};
  double fee_{0};

  double buy_lose_qty_{0};
  double buy_lose_pnl_{0};
  double buy_profit_qty_{0};
  double buy_profit_pnl_{0};

  double sell_lose_qty_{0};
  double sell_lose_pnl_{0};
  double sell_profit_qty_{0};
  double sell_profit_pnl_{0};

  double unrealized_pnl_{0};

  double bfamt_{0};
  double bfqty_{0};
  double sfamt_{0};
  double sfqty_{0};

  double consecutive_buy_fill_qty_{0};
  double consecutive_sell_fill_qty_{0};
  int last_fill_side_;
  int64_t last_buy_fill_timestamp_{0};
  int64_t last_sell_fill_timestamp_{0};

  Bbo bbo_;
  mutable Fill buy_fill_;
  mutable Fill sell_fill_;

  std::list<Fill> fills_;
  std::list<BuySellPair> bsp_list_;

  std::vector<Fill*> buy_fills_;
  std::vector<Fill*> sell_fills_;

  FillStatsConfig config_;
  int64_t last_pos_check_time_{0};
  int64_t num_bbo_{0};
};

}  // namespace xguo
